﻿/* ***************************************************************************
 *  This file is part of the C# port of the Invivo AI challenge simulation environment.
 *  Copyright 2010 Colin Green
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program .  If not, see <http://www.gnu.org/licenses/>.
 * 
 * 
 * This file incorporates open sourced work authored by the following people: 
 *
 *   Box2DX Copyright (c) 2008 Ihar Kalasouski http://code.google.com/p/box2dx
 *   Box2D original C++ version Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
 * 
 */
/*
  Box2DX Copyright (c) 2008 Ihar Kalasouski http://code.google.com/p/box2dx
  Box2D original C++ version Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com

  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
  arising from the use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
*/

using System;
using System.Windows.Forms;
using Box2DX.Common;
using Box2DX.Dynamics;
using Tao.OpenGl;
using System.Diagnostics;
using SysMath = System.Math;

namespace FunkAIChallenge
{
    public partial class MainForm : Form
    {
        #region Instance Fields

        SimulationWorld _simWorld;
        PlayerInterface _playerIface;
        PlayerController _controller;

        DebugDraw _debugDraw;

        // World extents.
        float _worldX1, _worldY1, _worldX2, _worldY2;

        #endregion

        #region Constructor

        /// <summary>
        /// Construct the form.
        /// </summary>
        public MainForm()
        {
            InitializeComponent();

            // Setup the display window.
            openGlControl.InitializeContexts();
            SetView();

            // Create the box2d world.

            SimulationParameters simParams = new SimulationParameters();
            simParams._ballPos.Set(9f, 6f);
            simParams._ballVelocity = Utils.Polar2Cartesian(new Vec2(12.5f, (float)(SysMath.PI * 0.5)));

            _simWorld = new SimulationWorld(simParams);

            // Attach display window drawing routine to the box2d world.
            _debugDraw = new FunkAIChallenge.OpenGLDebugDraw();
			uint flags = 0;
			flags += (uint)DebugDraw.DrawFlags.Shape;
			flags += (uint)DebugDraw.DrawFlags.Joint;
			flags += (uint)DebugDraw.DrawFlags.Controller;
            //flags += (uint)DebugDraw.DrawFlags.CoreShape;
            //flags += (uint)DebugDraw.DrawFlags.Aabb;
            //flags += (uint)DebugDraw.DrawFlags.Obb;
            //flags += (uint)DebugDraw.DrawFlags.Pair;
            //flags += (uint)DebugDraw.DrawFlags.CenterOfMass;
			_debugDraw.Flags = (DebugDraw.DrawFlags)flags;
            _simWorld.SetDebugDraw(_debugDraw);

            // Create a player interface into the box2d world.
            _playerIface = _simWorld.CreatePlayerInterface();
            _controller = new StraightArmController(_playerIface);
            //_controller = new FunkTestController(playerIface);

            // Start the simulation timer.
            simTimer.Start();
        }

        #endregion

        #region Private Methods [Init/Misc]

        /// <summary>
        /// Initialises OpenGL graphics library. Here we set what area of the box2d world to draw and
        /// the size of the drawing window on screen.
        /// </summary>
        private void SetView()
        {
            int width = openGlControl.Width;
            int height = openGlControl.Height;

            Gl.glViewport(0, 0, width, height);
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            Gl.glLoadIdentity();

            // L/R/B/T
            SetWorldExtents(-10f, 10f, -0.5f, 10f);

            Gl.glMatrixMode(Gl.GL_MODELVIEW);
            Gl.glLoadIdentity();
        }

        private void SetWorldExtents(float x1,  float x2, float y1, float y2)
        {
            _worldX1 = x1;
            _worldY1 = y1;
            _worldX2 = x2;
            _worldY2 = y2;
            Glu.gluOrtho2D(x1, x2, y1, y2);
        }

        /// <summary>
        /// Convert screen coordinates to box2d world coords.
        /// </summary>
		private Vec2 ConvertScreenToWorld(float x, float y)
		{
			float ctrlWidth = openGlControl.Width;
			float ctrlHeight = openGlControl.Height;

			float xProp = x / ctrlWidth;
			float yProp = (ctrlHeight - y) / ctrlHeight;

			Vec2 p = new Vec2();
            p.X = _worldX1 + (xProp * (_worldX2-_worldX1));
            p.Y = _worldY1 + (yProp * (_worldY2-_worldY1));
			return p;
		}

        #endregion

        #region Private Methods [Event Handling/Simulation Ticks]

        private void SimTickEvent(object sender,EventArgs e)
        {
            SimStep();
        }

        /// <summary>
        /// Moves the simulation forward by one timestep.
        /// </summary>
        private void SimStep()
        {
            // Clear viewport buffer.
			Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);

            // Move world forward one timestep.
            _simWorld.Step();

            // Invoke the player controller. 
            // Here it gets to read world state and modify the torques applied to the player arm.
            _controller.Step();

            // Draw the world in its new/current state.
			openGlControl.Draw();

            // Show hand pos in GUI.
            Vec2 handPos = Utils.Cartesian2Polar(_playerIface.HandPosition - _playerIface.ArmBasePosition);
            txtHandPos.Text = string.Format("({0:0.00},{1:0.00})", handPos.X, handPos.Y);
        }

        /// <summary>
        /// Mouse interaction with the box2d world.
        /// </summary>
        private void openGlControl_MouseDown(object sender,MouseEventArgs e)
        {
			if (e.Button == MouseButtons.Left) {
				_simWorld.MouseDown(ConvertScreenToWorld(e.X, e.Y));
            }
        }

        /// <summary>
        /// Mouse interaction with the box2d world.
        /// </summary>
        private void openGlControl_MouseMove(object sender,MouseEventArgs e)
        {
			Vec2 p = ConvertScreenToWorld(e.X, e.Y);
			_simWorld.MouseMove(p);
        }

        /// <summary>
        /// Mouse interaction with the box2d world.
        /// </summary>
        private void openGlControl_MouseUp(object sender,MouseEventArgs e)
        {
            _simWorld.MouseUp();
        }

        /// <summary>
        /// Viewport resize.
        /// </summary>
        private void openGlControl_Resize(object sender,EventArgs e)
        {
            SetView();
        }

        #endregion
    }
}
